package com.study.shiro.config;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.LinkedHashMap;

/**
 * @author huang guang yue
 * @version v1.0.0
 * @date 2021 2021/3/24 10:20
 */
@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean filterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 对于需要登陆的接口，如果没有登陆，访问指定的路径（可以是页面也可以是接口，如果是接口，可以进行提示）
        shiroFilterFactoryBean.setLoginUrl("/pub/need_login");

        // 登陆成功，跳转的地址(如果前后分离，则不需要。因为前端自己跳转首页)
        // shiroFilterFactoryBean.setSuccessUrl("/page/index.html");

        // 登陆成功，但是没有权限
        shiroFilterFactoryBean.setUnauthorizedUrl("/pub/not_perm");

        // 设置自定义 Filter
        LinkedHashMap<String, Filter> customerFilter = new LinkedHashMap<>();
        customerFilter.put("customerRoles", new CustomerRolesFilter());
        customerFilter.put("customerPerms", new CustomerPermsFilter());
        shiroFilterFactoryBean.setFilters(customerFilter);

        // 使用 LinkedHashMap，保证顺序，不然会出现灵异事件
        LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();

        // 退出
        filterMap.put("/logout", "logout");

        // 可以匿名访问
        filterMap.put("/pub/**", "anon");

        // 登陆即可访问，无角色和权限校验
        filterMap.put("/authc/**", "authc");

        // 需要具有某种角色才能访问（默认的 roles[admin,root] 是必须同事具有 admin 和 root，自定义过滤器之后只要其中之一即可）
        filterMap.put("/role/admin/**", "customerRoles[admin,root]");

        // 需要具有某种权限才能访问（同上，自定义过滤器，具有其中一个权限即可访问）
        filterMap.put("/perm/product/update/**", "customerPerms[updateProduct,productManager]");

        // 其余的接口需要登陆才能访问
        filterMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 使用 redis 进行缓存
     *
     */
    public RedisManager getRedisManager(){
        RedisManager redisManager = new RedisManager();
        redisManager.setHost("39.106.79.62");
        redisManager.setPort(6379);
        return redisManager;
    }

    /**
     * 缓存管理器
     */
    public RedisCacheManager cacheManager(){
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(getRedisManager());

        //设置过期时间，单位是秒，半小时
//        redisCacheManager.setExpire(60*60*30);

        return redisCacheManager;
    }

    /**
     * SecurityManager
     */
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置 sessionManager（如果不是前后分离，不用设置 sessionManager）
        securityManager.setSessionManager(sessionManager());
        // 设置缓存
        securityManager.setCacheManager(cacheManager());
        // 设置 realm。不能 new .(securityManager.setRealm(new CustomerRealm()))
        securityManager.setRealm(customerRealm());
        return securityManager;
    }

    /**
     * 自定义 realm
     */
    @Bean
    public CustomerRealm customerRealm(){
        CustomerRealm customerRealm = new CustomerRealm();
         customerRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return customerRealm;
    }

    /**
     * realm 加密器
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        // 密码使用 md5 加密
        matcher.setHashAlgorithmName("md5");
        // md5 两次
        matcher.setHashIterations(2);
        return matcher;
    }

    /**
     * 自定义 SessionManager
     */
    @Bean
    public SessionManager sessionManager(){
        CustomerSessionManager customerSessionManager = new CustomerSessionManager();
        // 超时时间，默认 30 分钟，单位毫秒（登陆之后没有任何操作，30 分钟之后就过期，有了操作就延长 30 分钟，这里设置 10 秒，方便测试）
//        customerSessionManager.setGlobalSessionTimeout(100000);
        customerSessionManager.setSessionDAO(redisSessionDAO());
        return customerSessionManager;
    }

    /**
     * 自定义 sessionDAO，持久化 session，服务重启 session 不失效
     */
    @Bean
    public RedisSessionDAO redisSessionDAO(){
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(getRedisManager());
        redisSessionDAO.setSessionIdGenerator(new CustomerSessionIdGenerator());
        return redisSessionDAO;
    }

    /**
     * 管理一些 shiro 的 bean 的生命周期
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 使 shiro 的注解生效, 比如 @RequireRoles
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
        return new AuthorizationAttributeSourceAdvisor();
    }

    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        return defaultAdvisorAutoProxyCreator;
    }
}
